#if !defined(PROCESS_POOL_H)
#define PROCESS_POOL_H

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <signal.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <sys/wait.h>

/// @brief 信号处理管道
static int sig_pipefd[2];

/********************* 辅助函数 **********************/

/// @brief 设置非阻塞管理符
/// @param fd 文件管理符
/// @return 原管理符属性
static int setNonblocking(int fd)
{
    int old_opt = fcntl(fd, F_GETFL);
    int new_opt = old_opt | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_opt);
    return old_opt;
}

/// @brief 添加FD到内核事件表
/// @param epollfd 要加入的内核事件表
/// @param fd 要操作的管理符
static void addFd(int epollfd, int fd)
{
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setNonblocking(fd);
}

/// @brief 从内核事件表删除FD上所有注册事件
/// @param epollfd 要加入的内核事件表
/// @param fd 新操作的管理符
static void removeFd(int epollfd, int fd)
{
    epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, 0);
    close(fd);
}

/// @brief 信号处理函数
/// @param sig 绑定信号
static void sig_handler(int sig)
{
    int save_errno = errno;
    int msg = sig;
    send(sig_pipefd[1], (char *)&msg, 1, 0);
    errno = save_errno;
}

/// @brief 设置信号处理函数
/// @param sig 信号值
/// @param hanlder 信号处理函数
/// @param restart 是否自动重启被该信号中断的某些系统调用
static void addSig(int sig, void(hanlder)(int), bool restart = true)
{
    struct sigaction sa;
    memset(&sa, '\0', sizeof(sa));
    sa.sa_handler = hanlder;
    if (restart)
        sa.sa_flags |= SA_RESTART;
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig, &sa, NULL) != -1);
}

/********************* 实现 **********************/

/// @brief 子进程描述类
class process
{
public:
    process() : m_pid(-1) {}

public:
    /// @brief 目标子进程PID
    pid_t m_pid;
    /// @brief 父/子进程通讯管道
    int m_pipefd[2];
};

/// @brief 进程池类
/// @tparam T 处理逻辑类
template <class T>
class processpool
{
private:
    /// @brief 最大子进程数
    static const int MAX_PROCESS_NUM = 16;
    /// @brief 子进程最大处理客户数
    static const int USER_PER_PROCESS = 65536;
    /// @brief epoll最大处理数
    static const int MAX_EVENT_NUM = 10000;

    /// @brief 池中进程总数
    int m_process_num;
    /// @brief 子进程在池中序号（从0开始）
    int m_idx;
    /// @brief epoll内核事件表
    int m_epollfd;
    /// @brief 监听socket
    int m_listenfd;
    /// @brief 是否停止运行
    int m_stop;

    /// @brief 所有子进程描述信息
    process *m_sub_process;
    /// @brief 进程池静态实例
    /// @note 实例默认空指针
    static processpool<T> *m_instance;

private:
    /// @brief 进程池构造函数 (私有)
    /// @tparam T 逻辑任务类
    /// @param listenfd 监听socket (创建进程池前被创建)
    /// @param process_num 进程池中子进程数量
    processpool(int listenfd, int process_num = 8);

public:
    /// @brief 进程池销毁函数
    ~processpool()
    {
        delete[] m_sub_process;
    }

public:
    /// @brief getInstance (获取唯一实例)
    /// @param listenfd 监听socket
    /// @param process_num 进程池中子进程数量
    /// @return 进程池实例
    static processpool<T> *create(int listenfd, int process_num = 8)
    {
        // 只存在单一进程池实例
        if (!m_instance)
            m_instance = new processpool<T>(listenfd, process_num);
        return m_instance;
    }

private:
    /// @brief 统一事件源
    /// @tparam T 逻辑任务类
    void setup_sig_pipe();

    /// @brief 运行父进程
    /// @tparam T 处理逻辑类
    void run_parent();

    /// @brief 运行子进程
    /// @tparam T 处理逻辑类
    void run_child();

public:
    /// @brief 运行进程池
    /// @tparam T 处理逻辑类
    void run();
};

template <class T>
processpool<T> *processpool<T>::m_instance = NULL;

template <class T>
processpool<T>::processpool(int listenfd, int process_num) : m_listenfd(listenfd), m_process_num(process_num), m_idx(-1), m_stop(false)
{
    assert((process_num > 0) && (process_num <= MAX_PROCESS_NUM));
    m_sub_process = new process[process_num];
    assert(m_sub_process);
    // 创建process_num个子进程，并与父进程建立管道
    for (int i = 0; i < process_num; ++i)
    {
        int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, m_sub_process[i].m_pipefd);
        assert(ret == 0);
        m_sub_process[i].m_pid = fork();
        assert(m_sub_process[i].m_pid >= 0);
        if (m_sub_process[i].m_pid > 0)
        {
            close(m_sub_process[i].m_pipefd[1]);
            continue;
        }
        else
        {
            close(m_sub_process[i].m_pipefd[0]);
            m_idx = i;
            break;
        }
    }
}

template <class T>
void processpool<T>::setup_sig_pipe()
{
    // 创建epoll事件监听表
    m_epollfd = epoll_create(5);
    assert(m_epollfd != -1);
    // 创建信号管道
    int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sig_pipefd);
    assert(ret != -1);
    setNonblocking(sig_pipefd[1]);
    addFd(m_epollfd, sig_pipefd[0]);
    // 设置信号处理函数
    addSig(SIGCHLD, sig_handler);
    addSig(SIGTERM, sig_handler);
    addSig(SIGINT, sig_handler);
    addSig(SIGPIPE, SIG_IGN);
}

template <class T>
void processpool<T>::run()
{
    // 父进程idx为-1，子进程中idx>=0
    if (m_idx != -1)
    {
        run_child();
        return;
    }
    run_parent();
}

template <class T>
void processpool<T>::run_child()
{
    setup_sig_pipe();
    // 子进程通过进程池中序号idx找到与父进程的通信管道
    int pipefd = m_sub_process[m_idx].m_pipefd[1];
    // 子进程监听pipefd，父进程通过其通知子进程accept新连接
    addFd(m_epollfd, pipefd);
    epoll_event events[MAX_EVENT_NUM];

    T *users = new T[USER_PER_PROCESS];
    assert(users);
    int num = 0;
    int ret = -1;
    while (!m_stop)
    {
        num = epoll_wait(m_epollfd, events, MAX_EVENT_NUM, -1);
        if ((num < 0) && (errno != EINTR))
        {
            printf("Epoll Failure\n");
            break;
        }
        for (int i = 0; i < num; i++)
        {
            int sockfd = events[i].data.fd;
            if ((sockfd == pipefd) && (events[i].events & EPOLLIN))
            {
                int client = 0;
                // 从父子之间管道读取数据，并保存到client中
                ret = recv(sockfd, (char *)&client, sizeof(client), 0);
                // 若读取成功，表示有新用户连接
                if (((ret < 0) && (errno != EAGAIN)) || ret == 0)
                    continue;
                else
                {
                    struct sockaddr_in client_address;
                    socklen_t client_addrlen = sizeof(client_address);
                    int connfd = accept(m_listenfd, (sockaddr *)&client_address, &client_addrlen);
                    if (connfd < 0)
                    {
                        perror("Accept");
                        continue;
                    }
                    addFd(m_epollfd, connfd);
                    // T类必须实现init方法，以初始化客户连接
                    // 使用connfd索引逻辑处理对象
                    users[connfd].init(m_epollfd, connfd, client_address);
                }
            }
            // 处理子进程接收到的信号
            else if ((sockfd == sig_pipefd[0]) && (events[i].events & EPOLLIN))
            {
                int sig;
                char signals[1024];
                ret = recv(sig_pipefd[0], signals, sizeof(signals), 0);
                if (ret <= 0)
                    continue;
                else
                {
                    for (int i = 0; i < ret; ++i)
                    {
                        switch (signals[i])
                        {
                        case SIGCHLD:
                        {
                            pid_t pid;
                            int stat;
                            while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
                                continue;
                            break;
                        }
                        case SIGTERM:
                        case SIGINT:
                        {
                            m_stop = true;
                            break;
                        }
                        default:
                            break;
                        }
                    }
                }
            }
            // 若为其他可读数据（客户请求）到来
            else if (events[i].events & EPOLLIN)
            {
                // T类必须实现process方法，以处理任务
                users[sockfd].process();
            }
            else
                continue;
        }
    }
    delete[] users;
    users = nullptr;
    close(pipefd);

    // 应由m_listenfd创建者关闭此描述符，即"谁创建-谁销毁"原则
    // close(m_listenfd);

    close(m_epollfd);
}

template <class T>
void processpool<T>::run_parent()
{
    setup_sig_pipe();
    // 父进程监听m_listenfd
    addFd(m_epollfd, m_listenfd);
    epoll_event events[MAX_EVENT_NUM];
    int sub_process_counter = 0;
    int new_conn = 1;
    int number = 0;
    int ret = -1;
    while (!m_stop)
    {
        number = epoll_wait(m_epollfd, events, MAX_EVENT_NUM, -1);
        if ((number < 0) && (errno != EINTR))
        {
            printf("Epoll Failure\n");
            break;
        }
        for (int i = 0; i < number; ++i)
        {
            int sockfd = events[i].data.fd;
            if (sockfd == m_listenfd)
            {
                // 若有新连接到来，采用Round-Robin方法（轮询）进行分配
                int i = sub_process_counter;
                do
                {
                    if (m_sub_process[i].m_pid != -1)
                        break;
                    i = (i + 1) % m_process_num;
                } while (i != sub_process_counter);

                if (m_sub_process[i].m_pid == -1)
                {
                    m_stop = true;
                    break;
                }
                sub_process_counter = (i + 1) % m_process_num;
                send(m_sub_process[i].m_pipefd[0], (char *)&new_conn, sizeof(new_conn), 0);
                printf("Send Request To Child%d\n", i);
            }
            // 处理父进程接收信号
            else if ((sockfd == sig_pipefd[0]) && (events[i].events & EPOLLIN))
            {
                int sig;
                char signals[1024];
                ret = recv(sig_pipefd[0], signals, sizeof(signals), 0);
                if (ret <= 0)
                    continue;
                else
                {
                    for (int i = 0; i < ret; ++i)
                    {
                        switch (signals[i])
                        {
                        // 子进程释放前通知父进程
                        case SIGCHLD:
                        {
                            pid_t pid;
                            int stat;
                            while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
                            {
                                for (int i = 0; i < m_process_num; ++i)
                                {
                                    // 若池中第i个子进程退出，则进行标记
                                    if (m_sub_process[i].m_pid == pid)
                                    {
                                        printf("Child %d Join\n", i);
                                        close(m_sub_process[i].m_pipefd[0]);
                                        m_sub_process[i].m_pid = -1;
                                    }
                                }
                            }
                            // 若所有子进程均退出，则父进程也退出
                            m_stop = true;
                            for (int i = 0; i < m_process_num; ++i)
                                if (m_sub_process[i].m_pid != -1)
                                    m_stop = false;
                            break;
                        }
                        case SIGTERM:
                        case SIGINT:
                        {
                            // 若父进程接收到终止信号
                            printf("KILL ALL THE CHILD NOW\n");
                            for (int i = 0; i < m_process_num; ++i)
                            {
                                int pid = m_sub_process[i].m_pid;
                                if (pid != -1)
                                    // TODO: 通过发送OOB数据终止
                                    kill(pid, SIGTERM);
                            }
                            break;
                        }
                        default:
                            break;
                        }
                    }
                }
            }
            else
            {
                continue;
            }
        }
    }
    // 谁创建，谁删除
    // close(m_listenfd);
    close(m_epollfd);
}

#endif // PROCESS_POOL_H
